package org.wms.minbearwcs.serivce.mission.generate.inbound;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import org.wms.minbearwcs.dao.inbound.inf.InboundOrderDetailDao;
import org.wms.minbearwcs.dao.mission.inf.WcsMissionDao;
import org.wms.minbearwcs.dao.stock.inf.MaterialStockDao;
import org.wms.minbearwcs.dao.stock.inf.MaterialTypeDao;
import org.wms.minbearwcs.entity.DTO.inbound.mission.CallAgvToExchangeArea;
import org.wms.minbearwcs.entity.DTO.inbound.mission.InboundMissionPoint;
import org.wms.minbearwcs.entity.DTO.transfer.Tuple;
import org.wms.minbearwcs.entity.PO.wcs.inbound.InboundOrderDetail;
import org.wms.minbearwcs.entity.PO.wcs.inbound.status.InboundOrderDetailStatus;
import org.wms.minbearwcs.entity.PO.wcs.mission.WcsMission;
import org.wms.minbearwcs.entity.PO.wcs.mission.status.InboundMissionFor3Or4FloorStatus;
import org.wms.minbearwcs.entity.PO.wcs.mission.status.InboundMissionFor5Or6FloorStatus;
import org.wms.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import org.wms.minbearwcs.entity.PO.wcs.stock.MaterialType;
import org.wms.minbearwcs.observer.caster.WcsMulticaster;
import org.wms.minbearwcs.observer.event.DefaultEvent;
import org.wms.minbearwcs.observer.type.inbound.InboundEventType;
import org.wms.minbearwcs.serivce.mission.generate.MissionGenerator;
import org.wms.minbearwcs.serivce.mission.generate.type.MissionGenType;
import org.wms.minbearwcs.serivce.mission.storageProcessor.booking.Floor3Or4.Storage3Or4FloorBookingSelector;
import org.wms.minbearwcs.serivce.mission.storageProcessor.booking.Floor5Or6.Storage5Or6FloorBookingSelector;
import org.wms.minbearwcs.utils.ContentPool;
import org.wms.minbearwcs.utils.IdWorker;

import java.time.LocalDateTime;

@Slf4j
@Component
@RequiredArgsConstructor
public class InboundMissionGennerator implements MissionGenerator<String> {

    private final IdWorker idWorker;
    private final MaterialStockDao materialStockDao;
    private final MaterialTypeDao materialTypeDao;
    private final InboundOrderDetailDao inboundOrderDetailDao;
    private final WcsMulticaster wcsMulticaster;
    private final Storage3Or4FloorBookingSelector storage3Or4FloorBookingSelector;
    private final Storage5Or6FloorBookingSelector storage5Or6FloorBookingSelector;
    private final WcsMissionDao wcsMissionDao;
    private final TransactionTemplate transactionTemplate;

    private final Object lock = new Object();

    @Override
    public MissionGenType type() {
        return MissionGenType.INBOUND;
    }

    @Override
    public Tuple<String> generate(Object source) {
        CallAgvToExchangeArea missionSource = (CallAgvToExchangeArea) source;

        // 更新工单状态
        wcsMulticaster.multicastEvent(DefaultEvent.create(source), InboundEventType.CALL_AGV_TO_EXCHANGE_AREA);

        // 更新明细状态
        Tuple<String> res = parsingStatusChange(missionSource);
        if (!res.success) {
            return res;
        }


        Tuple<String> tuple = transactionTemplate.execute(status -> {
            try {
                //更新物料批次号
                String batchCode = missionSource.getInboundOrderCode() +
                        LocalDateTime.now().format(ContentPool.DATE_FORMAT_TO_DAY);


                String palletCode = missionSource.getPalletCode();
                materialStockDao.updateBatchCodeByPallet(palletCode, batchCode);

                String materialCode = materialStockDao.selectMaterialCodeByPallet(palletCode);

                // 判断物料信息有无维护,没有则生成3/4F任务
                MaterialType materialType = materialTypeDao.selectByMaterialCode(materialCode);
                if (materialType == null) {
                    if (storage3Or4FMissionCreate(missionSource)) {
                        return Tuple.tuple("没有找到对应的物料信息，物料将搬运到3或4楼储位", true);
                    } else {
                        return Tuple.tuple("没有找到对应的物料信息，并且3或4楼没有空闲储位", false);
                    }
                }


                // 5/6F同一批次有无空位
                // 5/6F同一工单大储位有无空位
                //  放到新的大储位
                if (storage5Or6FMissionCreate(missionSource, materialCode, batchCode)) {
                    return Tuple.tuple(null, true);
                }

                // 爆仓放3/4F
                if (storage3Or4FMissionCreate(missionSource)) {
                    return Tuple.tuple("5或6楼没有空闲储位，物料将搬运到3或4楼储位", true);
                } else {
                    return Tuple.tuple("3或4楼没有空闲储位", false);
                }

            } catch (Exception e) {
                log.error("栈板：{}，所属出库工单任务生成出错", missionSource.getPalletCode(), e);
                status.setRollbackOnly();
                return Tuple.tuple("出库工单任务生成出错", false);
            }
        });
        assert tuple != null;
        if (!tuple.success){
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.WAIT_FOR_CALL_AGV);
        }
        return tuple;

    }

    public Tuple<String> parsingStatusChange(CallAgvToExchangeArea missionSource) {
        return transactionTemplate.execute(status -> {
            try {
                if (InboundOrderDetailStatus.WAIT_FOR_CALL_AGV == inboundOrderDetailDao.getStatusById(missionSource.getAssociatedDetailId())) {
                    synchronized (lock) {
                        if (InboundOrderDetailStatus.WAIT_FOR_CALL_AGV == inboundOrderDetailDao.getStatusById(missionSource.getAssociatedDetailId())) {
                            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                                    InboundOrderDetailStatus.PARSING);
                            return Tuple.tuple(null, true);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("出库工单明细状态转换出错", e);
                status.setRollbackOnly();
                return Tuple.tuple("出库工单明细状态转换出错", false);
            }
            return Tuple.tuple("叫车任务已生成", false);
        });
    }


    public boolean storage3Or4FMissionCreate(CallAgvToExchangeArea missionSource) {
        Tuple<InboundMissionPoint> storage3Or4F = storage3Or4FloorBookingSelector.searchAndBooking(missionSource.getPalletCode(),missionSource.getStartPoint());
        if (storage3Or4F.success) {
            inboundOrderDetailDao.updateStoragePointById(missionSource.getAssociatedDetailId(), storage3Or4F.value.getStoragePoint());
            inboundOrderDetailDao.updateLiftRequestPoint(missionSource.getAssociatedDetailId(),storage3Or4F.value.getLiftRequestPoint());
            String missionId = "Task_Inbound_3/4F_" + idWorker.nextId();
            WcsMission wcs3Or4FMission = new WcsMission(null, missionId, missionSource.getInboundOrderCode(), MissionType.INBOUND_3_OR_4_FLOOR.name(),
                    InboundMissionFor3Or4FloorStatus.WAIT_FOR_DISPATCH.name(), LocalDateTime.now(), null, null,
                    1, missionSource.getAssociatedDetailId());
            wcsMissionDao.insert(wcs3Or4FMission);
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.PROCESSING);
            return true;
        } else {
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.WAIT_FOR_CALL_AGV);
            return false;
        }
    }

    public boolean storage5Or6FMissionCreate(CallAgvToExchangeArea missionSource,String materialCode,String batchCode){
        Tuple<InboundMissionPoint> storage5Or6F = storage5Or6FloorBookingSelector.searchAndBooking(materialCode, batchCode, missionSource.getPalletCode());
        if (storage5Or6F.success){
            Integer associatedDetailId = missionSource.getAssociatedDetailId();
            InboundMissionPoint inboundMissionPoint = storage5Or6F.value;
            inboundOrderDetailDao.updateExchangePointById(associatedDetailId,inboundMissionPoint.getExchangePoint());
            inboundOrderDetailDao.updateStoragePointById(associatedDetailId,inboundMissionPoint.getStoragePoint());
            inboundOrderDetailDao.updateLiftRequestPoint(missionSource.getAssociatedDetailId(),inboundMissionPoint.getLiftRequestPoint());
            String missionId = "Task_Inbound_5/6F_" + idWorker.nextId();
            WcsMission mission = new WcsMission(null, missionId, missionSource.getInboundOrderCode(), MissionType.INBOUND_5_OR_6_FLOOR.name(),
                    InboundMissionFor5Or6FloorStatus.WAIT_FOR_DISPATCH_EXCHANGE_STORAGE.name(), LocalDateTime.now(), null, null,
                    1, missionSource.getAssociatedDetailId());
            wcsMissionDao.insert(mission);
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.PROCESSING);
            return true;
        }else {
            inboundOrderDetailDao.updateStatusById(missionSource.getAssociatedDetailId(),
                    InboundOrderDetailStatus.WAIT_FOR_CALL_AGV);
            return false;
        }
    }


}
